home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Technology Seed / Jan. '98 ATS.toast / NavServices1.0b3 / Navigation Services SDK / Examples / Sampler / Sampler ƒ / drag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-01-12  |  22.6 KB  |  815 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        drag.c
  3.  
  4.     Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  5.  
  6. */
  7.  
  8. #pragma segment DocSeg
  9.  
  10. #ifndef __FOLDERS__
  11. #include <Folders.h>
  12. #endif
  13.  
  14. #ifndef __DRAG__
  15. #include <Drag.h>
  16. #endif
  17.  
  18. #ifndef __WINDOWS__
  19. #include <Windows.h>
  20. #endif
  21.  
  22. #ifndef Common_Defs
  23. #include "Common.h"
  24. #endif
  25.  
  26. static long        caretTime;
  27. static short    caretOffset, caretShow, lastOffset, insertPosition, canAcceptItems;
  28. static short    cursorInContent;
  29.  
  30. #define    gCaretTime    ((short)*((long*)0x02F4))    // provides access to TextEdit's caretTime.
  31.  
  32. short HitTest(Point theLoc, Document** theDoc);
  33. void DrawCaret(short offset, TEHandle theTE);
  34. char GetCharAtOffset(short offset, TEHandle theTE);
  35. Boolean WhiteSpace(char theChar);
  36. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE);
  37. void InsertTextAtOffset(short offset, char* theBuf, long size, StScrpHandle theStyl, TEHandle theTE);
  38. short GetSelectionSize(Document* theDocument);
  39. Ptr GetSelectedTextPtr(Document* theDocument);
  40.  
  41. pascal OSErr MySendDataProc(FlavorType theType, void* refCon, ItemReference theItem, DragReference theDrag);
  42. pascal OSErr MyDrawingProc(DragRegionMessage message, RgnHandle showRgn, Point showOrigin, RgnHandle hideRgn, Point hideOrigin, void* dragDrawingRefCon, DragReference theDragRef);
  43. pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon, DragReference theDrag);
  44. pascal OSErr MyTrackingHandler(short message, WindowPtr theWindow, void* handlerRefCon, DragReference theDrag);
  45.  
  46. Boolean DropLocationIsFinderTrash(AEDesc* dropLocation);
  47.  
  48. extern DragSendDataUPP sendHandler;
  49.  
  50.  
  51. // *****************************************************************************
  52. // *
  53. // *    MyDrawingProc()
  54. // *
  55. // *****************************************************************************
  56. pascal OSErr MyDrawingProc(DragRegionMessage message, RgnHandle showRgn, Point /*showOrigin*/, RgnHandle hideRgn, Point /*hideOrigin*/, void* /*dragDrawingRefCon*/, DragReference /*theDragRef*/)
  57. {    
  58.     OSErr        result = paramErr;
  59.     RgnHandle    tempRgn;
  60.  
  61.     switch(message)
  62.         {
  63.         case kDragRegionBegin:
  64.             result = noErr;
  65.             break;
  66.  
  67.         case kDragRegionDraw:
  68.             XorRgn(showRgn,hideRgn,tempRgn = NewRgn());
  69.             InvertRgn(tempRgn);
  70.             DisposeRgn(tempRgn);
  71.             result = noErr;
  72.             break;
  73.  
  74.         case kDragRegionHide:
  75.             InvertRgn(hideRgn);
  76.             result = noErr;
  77.             break;
  78.         }
  79.     return result;
  80. }
  81.  
  82.  
  83. // *****************************************************************************
  84. // *
  85. // *    HitTest()
  86. // *
  87. // *    Given a point in global coordinates, HitTest returns a pointer to a
  88. // *    document structure if the point is inside a document window on the screen.
  89. // *    If the point is not inside a document window, HitTest return NULL in
  90. // *    theDoc. If the point is in a doument window and also in the viewRect of
  91. // *    the document's TextEdit field, HitTest also returns the offset into
  92. // *    the text that corresponds to that point. If the point is not in the text,
  93. // *    HitTest returns -1.
  94. // *
  95. // *****************************************************************************
  96. short HitTest(Point theLoc, Document** theDoc)
  97. {    
  98.     WindowPtr    theWindow;
  99.     short        offset;
  100.  
  101.     *theDoc = 0L;
  102.     offset = -1;
  103.  
  104.     if (FindWindow(theLoc,&theWindow) == inContent)
  105.         {
  106.         if (*theDoc = IsDocumentWindow(theWindow))
  107.             {
  108.             SetPort(theWindow);
  109.             GlobalToLocal(&theLoc);
  110.  
  111.             if ((PtInRect(theLoc, &(**((**theDoc).theTE)).viewRect)) && 
  112.                 (PtInRect(theLoc, &(**((**theDoc).theTE)).destRect)))
  113.                 {
  114.                 offset = TEGetOffset(theLoc,(**theDoc).theTE);
  115.  
  116.                 if ((TEIsFrontOfLine(offset,(**theDoc).theTE)) && (offset) &&            
  117.                         ((*((**((**theDoc).theTE)).hText))[offset - 1] != 0x0D) &&
  118.                         (TEGetPoint(offset - 1,(**theDoc).theTE).h < theLoc.h))
  119.                         {
  120.                         offset--;
  121.                         }
  122.                 }
  123.             }
  124.         }
  125.     return offset;
  126. }
  127.  
  128.  
  129. // *****************************************************************************
  130. // *
  131. // *    DrawCaret()
  132. // *
  133. // *    Draws a caret in a TextEdit field at the given offset. DrawCaret
  134. // *    expects the port to be set to the port that the TextEdit field is in.
  135. // *    DrawCaret inverts the image of the caret onto the screen.
  136. // *
  137. // *****************************************************************************
  138. void DrawCaret(short offset, TEHandle theTE)
  139. {    
  140.     Point    theLoc;
  141.     short    theLine, lineHeight;
  142.  
  143.     // get the coordinates and the line of the offset to draw the caret
  144.     theLoc  = TEGetPoint(offset,theTE);
  145.     theLine = TEGetLine(offset,theTE);
  146.  
  147.     //    For some reason, TextEdit dosen't return the proper coordinates
  148.     //    of the last offset in the field if the last character in the record
  149.     //    is a carriage return. TEGetPoint returns a point that is one line
  150.     //    higher than expected. The following code fixes this problem.
  151.     if ((offset == (**theTE).teLength) && (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D)
  152.         theLoc.v += TEGetHeight(theLine,theLine,theTE);
  153.  
  154.     PenMode(patXor);                                    //    invert the caret when drawing
  155.     lineHeight = TEGetHeight(theLine,theLine,theTE);    // get the height of the line that the offset points to
  156.  
  157.     MoveTo(theLoc.h - 1,theLoc.v - 1);
  158.     Line(0,1 - lineHeight);                                // draw the appropriate caret image
  159.  
  160.     PenNormal();
  161. }
  162.  
  163.  
  164. // *****************************************************************************
  165. // *
  166. // *    GetCharAtOffset()
  167. // *
  168. // *****************************************************************************
  169. char GetCharAtOffset(short offset, TEHandle theTE)
  170. {
  171.     if (offset < 0)
  172.         return(0x0D);
  173.     return(((char *) *((**theTE).hText))[offset]);
  174. }
  175.  
  176.  
  177. // *****************************************************************************
  178. // *
  179. // *    WhiteSpace()
  180. // *
  181. // *****************************************************************************
  182. Boolean WhiteSpace(char theChar)
  183. {
  184.     return((theChar == ' ') || (theChar == 0x0D));
  185. }
  186.  
  187.  
  188. // *****************************************************************************
  189. // *
  190. // *    WhiteSpaceAtOffset()
  191. // *
  192. // *****************************************************************************
  193. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE)
  194. {    
  195.     char theChar;
  196.  
  197.     if ((offset < 0) || (offset > (**theTE).teLength - 1))
  198.         return(true);
  199.  
  200.     theChar = ((char *) *((**theTE).hText))[offset];
  201.     return((theChar == ' ') || (theChar == 0x0D));
  202. }
  203.  
  204.  
  205. // *****************************************************************************
  206. // *
  207. // *    InsertTextAtOffset()
  208. // *
  209. // *****************************************************************************
  210. void InsertTextAtOffset(short offset, char* theBuf, long size, StScrpHandle theStyl, TEHandle theTE)
  211. {
  212.     if (size == 0)
  213.         return;
  214.  
  215.     //    If inserting at the end of a word and the selection does not begin with
  216.     //    a space, insert a space before the insertion.
  217.     if (!WhiteSpaceAtOffset(offset - 1,theTE) && WhiteSpaceAtOffset(offset,theTE) && !WhiteSpace(theBuf[0]))
  218.         {
  219.         TESetSelect(offset,offset,theTE);
  220.         TEKey(' ',theTE);
  221.         offset++;
  222.         }
  223.  
  224.     //    If inserting at the beginning of a word and the selection does not end
  225.     //    with a space, insert a space after the insertion.
  226.     if (WhiteSpaceAtOffset(offset - 1, theTE) && !WhiteSpaceAtOffset(offset, theTE) && !WhiteSpace(theBuf[size - 1]))
  227.         {
  228.         TESetSelect(offset, offset,theTE);
  229.         TEKey(' ',theTE);
  230.         }
  231.  
  232.     TESetSelect(offset,offset,theTE);
  233.     TEStyleInsert(theBuf,size,theStyl,theTE);
  234.     TESetSelect(offset,offset + size,theTE);
  235. }
  236.  
  237.  
  238. // *****************************************************************************
  239. // *
  240. // *    GetSelectionSize()
  241. // *
  242. // *****************************************************************************
  243. short GetSelectionSize(Document* theDocument)
  244. {
  245.     return((**(theDocument->theTE)).selEnd - (**(theDocument->theTE)).selStart);
  246. }
  247.  
  248.  
  249. // *****************************************************************************
  250. // *
  251. // *    GetSelectedTextPtr()
  252. // *
  253. // *****************************************************************************
  254. Ptr GetSelectedTextPtr(Document* theDocument)
  255. {
  256.     return((*(**(theDocument->theTE)).hText) + (**(theDocument->theTE)).selStart);
  257. }
  258.  
  259.  
  260. // *****************************************************************************
  261. // *
  262. // *    MySendDataProc()
  263. // *
  264. // *    Will provide 'styl' data for the drag when requested.
  265. // *
  266. // *****************************************************************************
  267. pascal OSErr MySendDataProc(FlavorType theType, void* refCon, ItemReference theItem, DragReference theDrag)
  268. {    
  269.     Document*        theDocument = (Document*)refCon;
  270.     StScrpHandle    theStyl;
  271.  
  272.     if (theType == 'styl') 
  273.         {
  274.         theStyl = TEGetStyleScrapHandle(theDocument->theTE);
  275.  
  276.         // Call SetDragItemFlavorData to provide the requested data.
  277.         HLock((Handle) theStyl);
  278.         SetDragItemFlavorData(theDrag,theItem,'styl',(Ptr)*theStyl,GetHandleSize((Handle)theStyl),0L);
  279.         HUnlock((Handle)theStyl);
  280.         DisposeHandle((Handle)theStyl);
  281.         } 
  282.     else
  283.         return badDragFlavorErr;
  284.  
  285.     return noErr;
  286. }
  287.  
  288.  
  289. // *****************************************************************************
  290. // *
  291. // *    MyReceiveDropHandler()
  292. // *
  293. // *****************************************************************************
  294. pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon, DragReference theDrag)
  295. {    
  296.     OSErr                result;
  297.     TEHandle            tempTE;
  298.     Rect                theRect, srcRect;
  299.     unsigned short        items, index;
  300.     ItemReference        theItem;
  301.     DragAttributes        attributes;
  302.     Ptr                    textData;
  303.     StScrpHandle        stylHandle;
  304.     Size                textSize, stylSize;
  305.     short                offset, selStart, selEnd, mouseDownModifiers, mouseUpModifiers, moveText;
  306.     Document*            theDocument = (Document*)handlerRefCon;
  307.     Point                thePoint;
  308.  
  309.     if ((!canAcceptItems) || (insertPosition == -1))
  310.         return(dragNotAcceptedErr);
  311.  
  312.     SetPort(theWindow);
  313.  
  314.     GetDragAttributes(theDrag, &attributes);
  315.     GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
  316.  
  317.     moveText = (attributes & kDragInsideSenderWindow) &&
  318.                (!((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)));
  319.  
  320.     //    Loop through all of the drag items contained in this drag and collect the text
  321.     //    into the tempTE record.
  322.     
  323.     SetRect(&theRect,0,0,0,0);
  324.     tempTE = TEStyleNew(&theRect,&theRect);
  325.  
  326.     CountDragItems(theDrag, &items);
  327.  
  328.     for (index = 1; index <= items; index++)
  329.         {
  330.         GetDragItemReferenceNumber(theDrag, index, &theItem);
  331.  
  332.         //    get the flags for a 'TEXT' flavor. If this returns noErr,
  333.         //    then we know that a 'TEXT' flavor exists in the item.
  334.  
  335.         result = GetFlavorDataSize(theDrag, theItem,'TEXT',&textSize);
  336.  
  337.         if (result == noErr) 
  338.             {
  339.             textData = NewPtr(textSize);
  340.             if (textData == 0L)
  341.                 {
  342.                 TEDispose(tempTE);
  343.                 return(memFullErr);
  344.                 }
  345.  
  346.             GetFlavorData(theDrag,theItem,'TEXT',textData,&textSize,0L);
  347.  
  348.             // check for optional styl data for the TEXT.
  349.  
  350.             stylHandle = 0L;
  351.             result = GetFlavorDataSize(theDrag,theItem,'styl',&stylSize);
  352.             if (result == noErr)
  353.                 {
  354.                 stylHandle = (StScrpHandle)NewHandle(stylSize);
  355.                 if (stylHandle == 0L)
  356.                     {
  357.                     TEDispose(tempTE);
  358.                     DisposePtr(textData);
  359.                     return(memFullErr);
  360.                     }
  361.  
  362.                 HLock((Handle)stylHandle);
  363.                 GetFlavorData(theDrag,theItem,'styl',*stylHandle,&stylSize,0L);
  364.                 HUnlock((Handle)stylHandle);
  365.                 }
  366.  
  367.             // insert this drag item's text into the tempTE
  368.  
  369.             TESetSelect(32767,32767,tempTE);
  370.             TEStyleInsert(textData,textSize,stylHandle,tempTE);
  371.  
  372.             DisposePtr(textData);
  373.             if (stylHandle)
  374.                 DisposeHandle((Handle) stylHandle);
  375.             }
  376.         }
  377.  
  378.     // pull the TEXT and styl data out of the tempTE handle.
  379.  
  380.     textData = NewPtr(textSize = (**tempTE).teLength);
  381.     if (textData == 0L)
  382.         {
  383.         TEDispose(tempTE);
  384.         return(memFullErr);
  385.         }
  386.     BlockMove(*(**tempTE).hText,textData,textSize);
  387.  
  388.     TESetSelect(0,32767,tempTE);
  389.     stylHandle = TEGetStyleScrapHandle(tempTE);
  390.  
  391.     TEDispose(tempTE);
  392.  
  393.     // if we actually received text, insert it into the destination
  394.  
  395.     if (textSize != 0)
  396.         {
  397.         // if the caret or highlighting is on the screen, remove it/them
  398.  
  399.         offset = caretOffset;
  400.  
  401.         if (caretOffset != -1)
  402.             {
  403.             DrawCaret(caretOffset,theDocument->theTE);
  404.             caretOffset = -1;
  405.             }
  406.  
  407.         if (attributes & kDragHasLeftSenderWindow)
  408.             HideDragHilite(theDrag);
  409.  
  410.         //    If the drag occurred completely within the same window and the window is not
  411.         //    frontmost, bring the window forward and update its contents before completing
  412.         //    the drag.
  413.  
  414.         if ((attributes & kDragInsideSenderWindow) && (theDocument->theWindow != FrontWindow()))
  415.             {
  416.             SelectWindow(theDocument->theWindow);
  417.             UpdateWindow(theDocument);
  418.             TEActivate(theDocument->theTE);
  419.             }
  420.  
  421.         // if the window is not active, must activate TE before inserting
  422.         // text or the background hilite will not update correctly.
  423.  
  424.         if (!((WindowPeek) theDocument->theWindow)->hilited)
  425.             if (!IsWindowHilited(theDocument->theWindow))
  426.                 TEActivate(theDocument->theTE);
  427.  
  428.         // if this window is also the sender, delete source selection if no option key.
  429.         if (moveText)
  430.             {
  431.             selStart = (**(theDocument->theTE)).selStart;
  432.             selEnd   = (**(theDocument->theTE)).selEnd;
  433.             if ( WhiteSpaceAtOffset(selStart - 1, theDocument->theTE) &&
  434.                 !WhiteSpaceAtOffset(selStart, theDocument->theTE) &&
  435.                 !WhiteSpaceAtOffset(selEnd - 1, theDocument->theTE) &&
  436.                  WhiteSpaceAtOffset(selEnd, theDocument->theTE))
  437.                 {
  438.                 if (GetCharAtOffset(selEnd, theDocument->theTE) == ' ')
  439.                     (**(theDocument->theTE)).selEnd++;
  440.                 }
  441.             if (insertPosition > selStart)
  442.                 {
  443.                 insertPosition -= ((**(theDocument->theTE)).selEnd -
  444.                                    (**(theDocument->theTE)).selStart);
  445.                 }
  446.             srcRect = (**theDocument->hiliteRgn).rgnBBox;
  447.             TEDelete(theDocument->theTE);
  448.             }
  449.  
  450.         InsertTextAtOffset(insertPosition,textData,textSize,stylHandle,theDocument->theTE);
  451.  
  452.         TEGetHiliteRgn(theDocument->hiliteRgn, theDocument->theTE);
  453.  
  454.         //    If the text is moving (not copying) within the same window, provide a ZoomRects
  455.         //    from the source to the destination before revealing the reflowed text.
  456.  
  457.         if (moveText)
  458.             {
  459.             theRect = (**theDocument->hiliteRgn).rgnBBox;
  460.             thePoint.h = thePoint.v = 0;
  461.             
  462.             SetPort(theWindow);
  463.  
  464.             LocalToGlobal(&thePoint);
  465.             OffsetRect(&srcRect,thePoint.h,thePoint.v);
  466.             OffsetRect(&theRect,thePoint.h,thePoint.v);
  467.             ZoomRects(&srcRect,&theRect,12,kZoomDecelerate);
  468.             }
  469.         theDocument->dirty = true;
  470.         }
  471.  
  472.     DisposePtr(textData);
  473.  
  474.     if (stylHandle)
  475.         DisposeHandle((Handle)stylHandle);
  476.  
  477.     // undo the TEActivate, if needed
  478.     if (!IsWindowHilited(theDocument->theWindow))
  479.         if (!((WindowPeek) theDocument->theWindow)->hilited)
  480.             TEDeactivate(theDocument->theTE);
  481.  
  482.     (**(theDocument->theTE)).inPort = (GrafPtr)theDocument->theWindow;
  483.  
  484.     return noErr;
  485. }
  486.  
  487.  
  488. // *****************************************************************************
  489. // *
  490. // *    MyTrackingHandler()
  491. // *
  492. // *    This is the drag tracking handler for windows in the application.
  493. // *
  494. // *****************************************************************************
  495. pascal OSErr MyTrackingHandler(short message, WindowPtr /*theWindow*/, void* handlerRefCon, DragReference theDrag)
  496. {    
  497.     short                result, offset;
  498.     long                theTime = TickCount();
  499.     unsigned short        count, index;
  500.     unsigned long        flavorFlags, attributes;
  501.     ItemReference        theItem;
  502.     RgnHandle            theRgn;
  503.     Document*            theDocument = (Document*)handlerRefCon;
  504.     Document*            hitDoc;
  505.     Point                theMouse, localMouse;
  506.  
  507.     if ((message != kDragTrackingEnterHandler) && (!canAcceptItems))
  508.         return(noErr);
  509.  
  510.     GetDragAttributes(theDrag, &attributes);
  511.  
  512.     switch (message)
  513.         {
  514.         case kDragTrackingEnterHandler:
  515.  
  516.             //    We get called with this message the first time that a drag enters ANY
  517.             //    window in our application. Check to see if all of the drag items contain
  518.             //    TEXT. We only accept a drag if all of the items in the drag can be accepted.
  519.             
  520.             canAcceptItems = true;
  521.  
  522.             CountDragItems(theDrag, &count);
  523.  
  524.             for (index = 1; index <= count; index++)
  525.                 {
  526.                 GetDragItemReferenceNumber(theDrag,index,&theItem);
  527.  
  528.                 result = GetFlavorFlags(theDrag, theItem,'TEXT',&flavorFlags);
  529.  
  530.                 if (result != noErr)
  531.                     {
  532.                     canAcceptItems = false;
  533.                     break;
  534.                     }
  535.                 }
  536.             break;
  537.  
  538.         case kDragTrackingEnterWindow:
  539.  
  540.             //    We receive an EnterWindow message each time a drag enters one of our
  541.             //    application's windows. We initialize our global variables for tracking
  542.             //    the drag through the window.
  543.  
  544.             caretTime = theTime;
  545.             caretOffset = lastOffset = -1;
  546.             caretShow = true;
  547.  
  548.             cursorInContent = false;
  549.  
  550.             break;
  551.  
  552.         case kDragTrackingInWindow:
  553.  
  554.             //    We receive InWindow messages as long as the mouse is in one of our windows
  555.             //    during a drag. We draw the window highlighting and blink the insertion caret
  556.             //    when we get these messages.
  557.  
  558.             GetDragMouse(theDrag,&theMouse,0L);
  559.             localMouse = theMouse;
  560.             GlobalToLocal(&localMouse);
  561.  
  562.             //    Show or hide the window highlighting when the mouse enters or leaves the
  563.             //    TextEdit field in our window (we don't want to show the highlighting when
  564.             //    the mouse is over the window title bar or over the scroll bars).
  565.  
  566.             if (attributes & kDragHasLeftSenderWindow) 
  567.                 {
  568.                 if (PtInRect(localMouse, &(**(theDocument->theTE)).viewRect))
  569.                     {
  570.                     if (!cursorInContent)
  571.                         {
  572.                         RectRgn(theRgn = NewRgn(),&(**(theDocument->theTE)).viewRect);
  573.                         ShowDragHilite(theDrag,theRgn,true);
  574.                         DisposeRgn(theRgn);
  575.                         }
  576.                     cursorInContent = true;
  577.                     }
  578.                 else
  579.                     {
  580.                     if (cursorInContent)
  581.                         HideDragHilite(theDrag);
  582.                     cursorInContent = false;
  583.                     }
  584.                 }
  585.  
  586.             offset = HitTest(theMouse, &hitDoc);
  587.  
  588.             //    If this application is the sender, do not allow tracking through
  589.             //    the selection in the window that sourced the drag.
  590.  
  591.             if (attributes & kDragInsideSenderWindow)
  592.                 {
  593.                 if ((offset >= (**(theDocument->theTE)).selStart) &&
  594.                     (offset <= (**(theDocument->theTE)).selEnd))
  595.                         offset = -1;
  596.                 }
  597.  
  598.             if (hitDoc == theDocument)
  599.                 {
  600.                 insertPosition = offset;
  601.  
  602.                 //    Reset flashing counter if the offset has moved. This makes the
  603.                 //    caret blink only after the caret has stopped moving long enough.
  604.  
  605.                 if (offset != lastOffset)
  606.                     {
  607.                     caretTime = theTime;
  608.                     caretShow = true;
  609.                     }
  610.                 lastOffset = offset;
  611.  
  612.                 // flash caret
  613.  
  614.                 if (theTime - caretTime > gCaretTime)
  615.                     {
  616.                     caretShow = !caretShow;
  617.                     caretTime = theTime;
  618.                     }
  619.                 if (!caretShow)
  620.                     offset = -1;
  621.  
  622.                 // if caret offset has changed, move caret on screen
  623.  
  624.                 if (offset != caretOffset)
  625.                     {
  626.                     if (caretOffset != -1)
  627.                         {
  628.                         DrawCaret(caretOffset,theDocument->theTE);
  629.                         }
  630.                     if (offset != -1)
  631.                         {
  632.                         DrawCaret(offset,theDocument->theTE);
  633.                         }
  634.                     }
  635.  
  636.                 caretOffset = offset;
  637.                 }
  638.             else 
  639.                 {
  640.                 lastOffset = offset;
  641.                 insertPosition = -1;
  642.                 }
  643.             break;
  644.  
  645.         case kDragTrackingLeaveWindow:
  646.  
  647.             // if the caret is on the screen, remove it.
  648.  
  649.             if (caretOffset != -1)
  650.                 {
  651.                 DrawCaret(caretOffset,theDocument->theTE);
  652.                 caretOffset = -1;
  653.                 }
  654.  
  655.             // remove window highlighting, if showing.
  656.  
  657.             if ((cursorInContent) && (attributes & kDragHasLeftSenderWindow))
  658.                 HideDragHilite(theDrag);
  659.             break;
  660.  
  661.         case kDragTrackingLeaveHandler:
  662.             break;
  663.  
  664.         }
  665.     return noErr;
  666. }
  667.  
  668.  
  669. // *****************************************************************************
  670. // *
  671. // *    DropLocationIsFinderTrash()
  672. // *
  673. // *    Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  674. // *
  675. // *****************************************************************************
  676. Boolean DropLocationIsFinderTrash(AEDesc* dropLocation)
  677. {    
  678.     OSErr        result;
  679.     AEDesc        dropSpec;
  680.     FSSpec*        theSpec;
  681.     CInfoPBRec    thePB;
  682.     short        trashVRefNum;
  683.     long        trashDirID;
  684.  
  685.     //    Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
  686.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  687.  
  688.     if ((dropLocation->descriptorType != typeNull) &&
  689.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr))
  690.         {
  691.         HLock(dropSpec.dataHandle);
  692.         theSpec = (FSSpec*)*dropSpec.dataHandle;
  693.  
  694.         // get the directory ID of the given dropLocation object
  695.         thePB.dirInfo.ioCompletion = 0L;
  696.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  697.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  698.         thePB.dirInfo.ioFDirIndex = 0;
  699.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  700.  
  701.         result = PBGetCatInfoSync(&thePB);
  702.  
  703.         HUnlock(dropSpec.dataHandle);
  704.         AEDisposeDesc(&dropSpec);
  705.  
  706.         if (result != noErr)
  707.             return(false);
  708.  
  709.         // if the result is not a directory, it must not be the Trash.
  710.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  711.             return false;
  712.  
  713.         // get information about the Trash folder
  714.         FindFolder(theSpec->vRefNum,kTrashFolderType,kCreateFolder,&trashVRefNum,&trashDirID);
  715.  
  716.         //    if the directory ID of the dropLocation object is the same as the directory ID
  717.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  718.  
  719.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  720.             return true;
  721.         }
  722.     return false;
  723. }
  724.  
  725.  
  726. // *****************************************************************************
  727. // *
  728. // *    DragText()
  729. // *
  730. // *****************************************************************************
  731. short DragText(Document* theDocument, EventRecord* theEvent, RgnHandle hiliteRgn)
  732. {    
  733.     OSErr                theErr = noErr;
  734.     short                result;
  735.     RgnHandle            dragRegion, tempRgn;
  736.     Point                theLoc;
  737.     DragReference        theDrag;
  738.     StScrpHandle        theStyl;
  739.     AEDesc                dropLocation;
  740.     DragAttributes        attributes;
  741.     short                mouseDownModifiers, mouseUpModifiers, copyText;
  742.  
  743.     // copy the hilite region into dragRegion and offset it into global coordinates.
  744.     CopyRgn(hiliteRgn,dragRegion = NewRgn());
  745.     SetPt(&theLoc,0,0);
  746.     LocalToGlobal(&theLoc);
  747.     OffsetRgn(dragRegion,theLoc.h,theLoc.v);
  748.  
  749.     if (!WaitMouseMoved(theEvent->where))
  750.         return false;
  751.         
  752.     NewDrag(&theDrag);
  753.  
  754.     AddDragItemFlavor(theDrag,1,'TEXT',GetSelectedTextPtr(theDocument),GetSelectionSize(theDocument),0);
  755.  
  756.     theStyl = TEGetStyleScrapHandle(theDocument->theTE);
  757.     HLock((Handle) theStyl);
  758.     AddDragItemFlavor(theDrag,1,'styl',(Ptr)*theStyl,GetHandleSize((Handle)theStyl),0);
  759.     HUnlock((Handle) theStyl);
  760.     DisposeHandle((Handle)theStyl);
  761.  
  762.     sendHandler = NewDragSendDataProc(&MySendDataProc);
  763.     theErr = SetDragSendProc(theDrag,sendHandler,(void*)theDocument);
  764.  
  765.     SetDragItemBounds(theDrag,1,&(**dragRegion).rgnBBox);
  766.  
  767.     // prepare the drag region
  768.     tempRgn = NewRgn();
  769.     CopyRgn(dragRegion,tempRgn);
  770.     InsetRgn(tempRgn,1,1);
  771.     DiffRgn(dragRegion,tempRgn,dragRegion);
  772.     DisposeRgn(tempRgn);
  773.  
  774.     /*
  775.     // use this if you want custom draw outlines..
  776.     if (gCanDrag)
  777.         {
  778. #if USESROUTINEDESCRIPTORS
  779.         sendHandler = NewDragSendDataProc(&MyDrawingProc);
  780.         theErr = SetDragDrawingProc(theDrag,drawHandler,0L);
  781. #else
  782.         theErr = SetDragDrawingProc(theDrag,MyDrawingProc,0L);
  783. #endif
  784.         }
  785.     */
  786.  
  787.     result = TrackDrag(theDrag,theEvent,dragRegion);
  788.  
  789.     if (result != noErr && result != userCanceledErr)
  790.         return true;
  791.  
  792.     // check to see if the drop occurred in the Finder's Trash. If the drop occurred
  793.     // in the Finder's Trash and a copy operation wasn't specified, delete the source selection
  794.  
  795.     GetDragAttributes(theDrag,&attributes);
  796.     if (!(attributes & kDragInsideSenderApplication))
  797.         {
  798.         GetDropLocation(theDrag,&dropLocation);
  799.  
  800.         GetDragModifiers(theDrag,0L,&mouseDownModifiers,&mouseUpModifiers);
  801.         copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  802.  
  803.         if ((!copyText) && (DropLocationIsFinderTrash(&dropLocation)))
  804.             {
  805.             TEDelete(theDocument->theTE);
  806.             theDocument->dirty = true;
  807.             }
  808.         AEDisposeDesc(&dropLocation);
  809.         }
  810.  
  811.     DisposeDrag(theDrag);
  812.     DisposeRgn(dragRegion);
  813.  
  814.     return true;
  815. }